//
// Created by jack on 2021/2/2.
//
#include "weilapb_defines.h"

#include <WL.Service.pb.h>
#include <WLcJSON.h>
#include <md5.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include <stdio.h>

#include "weilapb_datafuns.h"

#include "weilapb.h"
#include "weilapb_error.h"
#include "weilapb_internal.h"
#include "weilapb_loginmsg.h"

void* weilapb_decodeStatus(pb_istream_t* stream, const pb_field_t* field) {
    WL_Common_UserStatus wlCommonUserStatus = WL_Common_UserStatus_init_zero;
    PBLinkItem*          pPbLinkItem        = (PBLinkItem*)PB_ALLOC(sizeof(PBLinkItem) + sizeof(PBUserStatus));
    if (pPbLinkItem) {
        PBUserStatus* pUserStatus = (PBUserStatus*)pPbLinkItem->data;
        pPbLinkItem->dataSize     = sizeof(PBUserStatus);
        if (pb_decode(stream, WL_Common_UserStatus_fields, &wlCommonUserStatus)) {
            if (wlCommonUserStatus.has_clientType) {
                pUserStatus->clientType = wlCommonUserStatus.clientType;
            }

            if (wlCommonUserStatus.has_statusType) {
                pUserStatus->statusType = wlCommonUserStatus.statusType;
            }

            if (wlCommonUserStatus.has_updated) {
                pUserStatus->updatedTimestamp = wlCommonUserStatus.updated;
            }
        } else {
            PB_FREE(pPbLinkItem);
            pPbLinkItem = PB_NULL;
            weilapb_error(ERR_DECODE, "decode status list fail:%s", stream->errmsg ? stream->errmsg : "unknown");
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate PBLinkItem failed");
    }

    return pPbLinkItem;
}

PBWeilaMsg* weilapb_decodeLoginRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBRspLogin));
    PBFieldData               nickField, avatarField, signatureField, numberField;
    PBFieldData               emailField, countryCodeField, phoneField, statusField, extAccountField, extPasswdField;
    PBFieldData               configField, verifiedCode, token, extensionConfigField;

    if (pWeilaMsg) {
        PBRspLogin* pRspLogin;
        pWeilaMsg->contentSize = sizeof(PBRspLogin);
        pRspLogin              = (PBRspLogin*)pWeilaMsg->pContent;

        istream = pb_istream_from_buffer(pData, dataSize);
        // 昵称
        weilapb_initDecDataItem(
            &nickField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_SHORT_STRING_LEN, (PB_UINT8*)pRspLogin->userInfo.nick);
        weilapb_setDecCallback(&nickField, &serviceMessage.loginMessage.rspLoginApp.userInfo.nick, PB_NULL);
        // 头像
        weilapb_initDecDataItem(
            &avatarField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_URL_STRING_LEN, (PB_UINT8*)pRspLogin->userInfo.avatar);
        weilapb_setDecCallback(&avatarField, &serviceMessage.loginMessage.rspLoginApp.userInfo.avatar, PB_NULL);
        // 微喇号
        weilapb_initDecDataItem(
            &numberField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_PHONE_NUM_LEN, (PB_UINT8*)pRspLogin->userInfo.number);
        weilapb_setDecCallback(&numberField, &serviceMessage.loginMessage.rspLoginApp.userInfo.number, PB_NULL);
        // Email
        weilapb_initDecDataItem(
            &emailField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_URL_STRING_LEN, (PB_UINT8*)pRspLogin->userInfo.email);
        weilapb_setDecCallback(&emailField, &serviceMessage.loginMessage.rspLoginApp.userInfo.email, PB_NULL);
        // 国家码
        weilapb_initDecDataItem(&countryCodeField,
                                PB_DATA_TYPE_FIXED_SIZE_STRING,
                                MAX_SHORTEST_STRING_LEN,
                                (PB_UINT8*)pRspLogin->userInfo.countryCode);
        weilapb_setDecCallback(
            &countryCodeField, &serviceMessage.loginMessage.rspLoginApp.userInfo.countryCode, PB_NULL);
        // 号码
        weilapb_initDecDataItem(
            &phoneField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_PHONE_NUM_LEN, (PB_UINT8*)pRspLogin->userInfo.phone);
        weilapb_setDecCallback(&phoneField, &serviceMessage.loginMessage.rspLoginApp.userInfo.phone, PB_NULL);
        // 签名
        weilapb_initDecDataItem(&signatureField,
                                PB_DATA_TYPE_FIXED_SIZE_STRING,
                                MAX_LONG_STRING_LEN,
                                (PB_UINT8*)pRspLogin->userInfo.signature);
        weilapb_setDecCallback(&signatureField, &serviceMessage.loginMessage.rspLoginApp.userInfo.signature, PB_NULL);
        // 绑定的账号
        weilapb_initDecDataItem(
            &extAccountField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_PHONE_NUM_LEN, (PB_UINT8*)pRspLogin->extAccount);
        weilapb_setDecCallback(&extAccountField, &serviceMessage.loginMessage.rspLoginApp.extensionAccount, PB_NULL);
        // 绑定的账号的密码
        weilapb_initDecDataItem(
            &extPasswdField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_SHORT_STRING_LEN, (PB_UINT8*)pRspLogin->extPass);
        weilapb_setDecCallback(&extPasswdField, &serviceMessage.loginMessage.rspLoginApp.extensionPassword, PB_NULL);
        // 配置信息
        weilapb_initDecDataItem(
            &configField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_LONG_STRING_LEN, (PB_UINT8*)pRspLogin->userConfig);
        weilapb_setDecCallback(&configField, &serviceMessage.loginMessage.rspLoginApp.userConfig, PB_NULL);
        // 验证码
        weilapb_initDecDataItem(
            &verifiedCode, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_VERIFY_CODE_LEN, (PB_UINT8*)pRspLogin->verificationCode);
        weilapb_setDecCallback(&verifiedCode, &serviceMessage.loginMessage.rspLoginApp.verificationCode, PB_NULL);

        // token
        weilapb_initDecDataItem(
            &token, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_M_SHORT_STRING_LEN, (PB_UINT8*)pRspLogin->token);
        weilapb_setDecCallback(&token, &serviceMessage.loginMessage.rspLoginApp.token, PB_NULL);

        // extension配置
        weilapb_initDecDataItem(&extensionConfigField,
                                PB_DATA_TYPE_FIXED_SIZE_STRING,
                                MAX_LONG_STRING_LEN,
                                (PB_UINT8*)pRspLogin->extensionConfig);
        weilapb_setDecCallback(
            &extensionConfigField, &serviceMessage.loginMessage.rspLoginApp.extensionConfig, PB_NULL);

        weilapb_initDecDataItem(&statusField, PB_DATA_TYPE_LINK_SUBMSG, 0, &pRspLogin->userStatusList);
        weilapb_setDecCallback(&statusField, &serviceMessage.loginMessage.rspLoginApp.statusList, PB_NULL);
        statusField.decSubMsg = weilapb_decodeStatus;

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            weilapb_addRecyclePointer(pWeilaMsg, RECYCLE_PTR_LINK, &pRspLogin->userStatusList);

            if (serviceMessage.loginMessage.rspLoginApp.has_userInfo) {
                if (serviceMessage.loginMessage.rspLoginApp.userInfo.has_userId) {
                    pRspLogin->userInfo.userId = serviceMessage.loginMessage.rspLoginApp.userInfo.userId;
                }

                if (serviceMessage.loginMessage.rspLoginApp.userInfo.has_status) {
                    pRspLogin->userInfo.status = serviceMessage.loginMessage.rspLoginApp.userInfo.status;
                }

                if (serviceMessage.loginMessage.rspLoginApp.userInfo.has_sex) {
                    pRspLogin->userInfo.sex = serviceMessage.loginMessage.rspLoginApp.userInfo.sex;
                }

                if (serviceMessage.loginMessage.rspLoginApp.userInfo.has_type) {
                    pRspLogin->userInfo.type = serviceMessage.loginMessage.rspLoginApp.userInfo.type;
                }

                if (serviceMessage.loginMessage.rspLoginApp.userInfo.has_created) {
                    pRspLogin->userInfo.createdTimestamp =
                        (PB_UINT32)serviceMessage.loginMessage.rspLoginApp.userInfo.created;
                }
            }

            if (serviceMessage.loginMessage.rspLoginApp.has_forbidDuration) {
                pRspLogin->forbiddenDuration = serviceMessage.loginMessage.rspLoginApp.forbidDuration;
            }

            if (serviceMessage.loginMessage.rspLoginApp.has_extensionState) {
                pRspLogin->extensionState = serviceMessage.loginMessage.rspLoginApp.extensionState;
            }

            if (serviceMessage.loginMessage.rspLoginApp.has_extensionManagerId) {
                pRspLogin->extManagerId = serviceMessage.loginMessage.rspLoginApp.extensionManagerId;
            }

            if (serviceMessage.loginMessage.rspLoginApp.has_serverTime) {
                pRspLogin->timestamp = serviceMessage.loginMessage.rspLoginApp.serverTime;
            }

            // if (serviceMessage.loginMessage.rspLoginApp.has_extensionConfig) {
            //     pRspLogin->extensionType = serviceMessage.loginMessage.rspLoginApp.extensionType;
            // }
        } else {
            weilapb_freePBLinkList(&pRspLogin->userStatusList);
            PB_FREE(pWeilaMsg);
            return PB_NULL;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeRefreshAccessTokenRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + MAX_LONG_STRING_LEN);

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBFieldData accessTokenField;
        PB_CHAR*    pAccessToken = (PB_CHAR*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize   = MAX_LONG_STRING_LEN;

        weilapb_initDecDataItem(&accessTokenField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_SHORT_STRING_LEN, pAccessToken);
        weilapb_setDecCallback(
            &accessTokenField, &serviceMessage.loginMessage.rspRefreshAccessToken.accessToken, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeHeartbeatMsg(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PB_UINT32));

    istream = pb_istream_from_buffer(pData, dataSize);
    if (pWeilaMsg) {
        PB_UINT32* pServerTime = (PB_UINT32*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize = sizeof(PB_UINT32);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.loginMessage.rspHeartbeat.has_serverTime) {
                *pServerTime = serviceMessage.loginMessage.rspHeartbeat.serverTime;
            } else if (serviceMessage.loginMessage.reqHeartbeat.has_serverTime) {
                *pServerTime = serviceMessage.loginMessage.reqHeartbeat.serverTime;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode lock failed");
            return pWeilaMsg;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }
    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeKickoutNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = (PBWeilaMsg*)PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PB_UINT32));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        pWeilaMsg->contentSize = sizeof(PB_UINT32);
        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            PB_UINT32* pReason = (PB_UINT32*)pWeilaMsg->pContent;
            if (serviceMessage.loginMessage.ntfKickout.has_reason) {
                *pReason = serviceMessage.loginMessage.ntfKickout.reason;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode weila msg");
            return pWeilaMsg;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeAnswerBindExtensionRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBFieldData               extAccountField, extPasswdField;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBRspAnswerBindExtension));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBRspAnswerBindExtension* pRspBindExt = (PBRspAnswerBindExtension*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize                = sizeof(PBRspAnswerBindExtension);
        // 绑定的账号
        weilapb_initDecDataItem(
            &extAccountField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_PHONE_NUM_LEN, (PB_UINT8*)pRspBindExt->account);
        weilapb_setDecCallback(&extAccountField, &serviceMessage.loginMessage.rspAnswerBindExtension.account, PB_NULL);
        // 绑定的账号的密码
        weilapb_initDecDataItem(
            &extPasswdField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_SHORT_STRING_LEN, (PB_UINT8*)pRspBindExt->password);
        weilapb_setDecCallback(&extPasswdField, &serviceMessage.loginMessage.rspAnswerBindExtension.password, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.loginMessage.rspAnswerBindExtension.has_extensionState) {
                pRspBindExt->extensionState = serviceMessage.loginMessage.rspAnswerBindExtension.extensionState;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode answer bind failed");
            return pWeilaMsg;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeBindExtensionNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBFieldData               nickField;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PBNtfBindExtension));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBNtfBindExtension* pNtfBindExtension = (PBNtfBindExtension*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize                = sizeof(PBNtfBindExtension);
        // 绑定的账号
        weilapb_initDecDataItem(
            &nickField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_SHORT_STRING_LEN, (PB_UINT8*)pNtfBindExtension->binderNick);
        weilapb_setDecCallback(&nickField, &serviceMessage.loginMessage.ntfBindExtension.binderNick, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.loginMessage.ntfBindExtension.has_binderId) {
                pNtfBindExtension->binderId = serviceMessage.loginMessage.ntfBindExtension.binderId;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode bind failed");
            return pWeilaMsg;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeLockExtensionNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PB_UINT32));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PB_UINT32* pExtensionState = (PB_UINT32*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize     = sizeof(PB_UINT32);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.loginMessage.ntfLockExtension.has_extensionState) {
                *pExtensionState = serviceMessage.loginMessage.ntfLockExtension.extensionState;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode lock failed");
            return pWeilaMsg;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeUnlockExtensionNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PB_UINT32));

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PB_UINT32* pExtensionState = (PB_UINT32*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize     = sizeof(PB_UINT32);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.loginMessage.ntfUnlockExtension.has_extensionState) {
                *pExtensionState = serviceMessage.loginMessage.ntfUnlockExtension.extensionState;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode lock failed");
            return pWeilaMsg;
        }
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeSetExtensionConfigNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + MAX_LONG_STRING_LEN);

    istream = pb_istream_from_buffer(pData, dataSize);
    if (pWeilaMsg) {
        PBFieldData contentField;
        PB_CHAR*    pConfig    = (PB_CHAR*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize = MAX_LONG_STRING_LEN;

        weilapb_initDecDataItem(&contentField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_LONG_STRING_LEN, pConfig);
        weilapb_setDecCallback(&contentField, &serviceMessage.loginMessage.ntfSetExtensionConfig.config, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeGetExtensionConfigRsp(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + MAX_LONG_STRING_LEN);

    istream = pb_istream_from_buffer(pData, dataSize);

    if (pWeilaMsg) {
        PBFieldData configInfoField;
        PB_CHAR*    pConfig    = (PB_CHAR*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize = MAX_LONG_STRING_LEN;

        weilapb_initDecDataItem(&configInfoField, PB_DATA_TYPE_FIXED_SIZE_STRING, MAX_LONG_STRING_LEN, pConfig);
        weilapb_setDecCallback(&configInfoField, &serviceMessage.loginMessage.rspGetExtensionConfig.config, PB_NULL);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg* weilapb_decodeShutdownExtensionNtf(const PB_UINT8* pData, PB_UINT32 dataSize) {
    WL_Service_ServiceMessage serviceMessage = WL_Service_ServiceMessage_init_zero;
    pb_istream_t              istream;
    PBWeilaMsg*               pWeilaMsg = PB_ALLOC(sizeof(PBWeilaMsg) + sizeof(PB_UINT32));

    istream = pb_istream_from_buffer(pData, dataSize);
    if (pWeilaMsg) {
        PB_UINT32* pGuardainId = (PB_UINT32*)pWeilaMsg->pContent;
        pWeilaMsg->contentSize = sizeof(PB_UINT32);

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            if (serviceMessage.loginMessage.ntfShutdownExtension.has_guardianId) {
                *pGuardainId = serviceMessage.loginMessage.ntfShutdownExtension.guardianId;
            }
        } else {
            PB_FREE(pWeilaMsg);
            pWeilaMsg = PB_NULL;
            weilapb_error(ERR_DECODE, "decode lock failed");
            return pWeilaMsg;
        }

        if (weilapb_decodeWeilaMsg(pWeilaMsg, &istream, &serviceMessage)) {
            return pWeilaMsg;
        }

        weilapb_error(ERR_DECODE, "decode msg data fail");
        PB_FREE(pWeilaMsg);
        pWeilaMsg = PB_NULL;
    } else {
        weilapb_error(ERR_OUT_OF_MEMORY, "allocate weila msg failed");
    }

    return pWeilaMsg;
}

PBWeilaMsg*
    weilapb_decodeLoginMsg(PB_INT32 commandId, PB_INT32 commandType, const PB_UINT8* pData, PB_UINT32 dataSize) {
    PBWeilaMsg* pWeilaMsg = PB_NULL;

    switch (commandId) {
        case WL_Login_LoginCommandId_LOGIN_COMMAND_LOGIN_APP: {
            pWeilaMsg = weilapb_decodeLoginRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_LOGIN_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_LOGOUT: {
            pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_LOGOUT_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_HEARTBEAT: {
            pWeilaMsg = weilapb_decodeHeartbeatMsg(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_HEARTBEAT_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_REFRESH_ACCESS_TOKEN: {
            pWeilaMsg = weilapb_decodeRefreshAccessTokenRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_REFRESH_ACCESS_TOKEN_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_KICKOUT: {
            if (commandType == WL_Service_CommandType_COMMAND_NOTIFY) {
                pWeilaMsg = weilapb_decodeKickoutNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_KICKOUT_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_ANSWER_BIND_EXTENSION: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeAnswerBindExtensionRsp(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_ANSWER_BIND_EXTENSION_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_BIND_EXTENSION: {
            if (commandType == WL_Service_CommandType_COMMAND_NOTIFY) {
                pWeilaMsg = weilapb_decodeBindExtensionNtf(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_BIND_EXTENSION_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_LOCK_EXTENSION: {
            if (commandType == WL_Service_CommandType_COMMAND_NOTIFY) {
                pWeilaMsg = weilapb_decodeLockExtensionNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_LOCK_EXTENSION;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_UNLOCK_EXTENSION: {
            if (commandType == WL_Service_CommandType_COMMAND_NOTIFY) {
                pWeilaMsg = weilapb_decodeUnlockExtensionNtf(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_UNLOCK_EXTENSION;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_UNBIND_EXTENSION: {
            if (commandType == WL_Service_CommandType_COMMAND_NOTIFY) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            }
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_UNBIND_EXTENSION_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_GET_EXTENSION_CONFIG: {
            pWeilaMsg = weilapb_decodeGetExtensionConfigRsp(pData, dataSize);
            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_GET_EXTENSION_CONFIG_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_SET_EXTENSION_CONFIG: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeSetExtensionConfigNtf(pData, dataSize);
            }

            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_SET_EXTENSION_CONFIG_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_SHUTDOWN_EXTENSION: {
            if (commandType == WL_Service_CommandType_COMMAND_RESPONSE) {
                pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);
            } else {
                pWeilaMsg = weilapb_decodeShutdownExtensionNtf(pData, dataSize);
            }

            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_SHUTDOWN_EXTENSION_MSG;
            }
        } break;

        case WL_Login_LoginCommandId_LOGIN_COMMAND_ANSWER_SHUTDOWN_EXTENSION: {
            pWeilaMsg = weilapb_decodeEmptyRsp(pData, dataSize);

            if (pWeilaMsg) {
                pWeilaMsg->message = WL_LOGIN_ANSWER_SHUTDOWN_EXTENSION_MSG;
            }
        } break;
    }

    return pWeilaMsg;
}

/////// Encoder Functions
PBEncResult weilapb_buildLogin(PB_UINT16 commandId, WL_Login_LoginMessage* pParam) {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_LOGIN, commandId, WL_Service_CommandType_COMMAND_REQUEST);
    memset(&pbResult, 0, sizeof(PBEncResult));
    pbResult.message = weilapb_getMessageByCommandId(WL_Service_ServiceID_SERVICE_LOGIN, commandId);
    if (pServiceMessage) {
        pServiceMessage->has_loginMessage = true;
        memcpy(&pServiceMessage->loginMessage, pParam, sizeof(WL_Login_LoginMessage));
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        } else {
            pbResult.errCode = ERR_ENCODE;
            PB_FREE(pServiceMessage);
        }
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildLoginReq(PBReqLoginParam* pParam) {
    return weilapb_buildLoginReqWithType(pParam, WL_Common_ClientType_CLIENT_EMBEDDED);
}

PBEncResult weilapb_buildLoginReqWithType(PBReqLoginParam* pParam, int type) {
    PBEncResult           pbResult;
    PBReqLoginExtParam*   pExtParam           = PB_NULL;
    WL_Login_LoginMessage wlLoginLoginMessage = WL_Login_LoginMessage_init_zero;
    WL_Login_ReqLoginApp* pReqLoginApp;
    PB_CHAR*              pSignature;
    WLcJSON_Hooks         hooks;
    WLcJSON*              signatureJson = NULL;
    PBFieldData           signatureField;
    PB_CHAR               passwdMd5[MAX_SHORT_STRING_LEN] = {0};
    PBFieldData           passwdField, accountField, countryCodeField, specialField, devVersionField;

    memset(&pbResult, 0, sizeof(PBEncResult));
    if (pParam->sizeOfStruct == sizeof(PBReqLoginExtParam)) {
        pExtParam = (PBReqLoginExtParam*)pParam;
    } else if (pParam->sizeOfStruct != sizeof(PBReqLoginParam)) {
        pbResult.errCode = ERR_INVALID_PARAMS;
        return pbResult;
    }

    if (!(pExtParam && strlen(pExtParam->appKey) > 0 && strlen(pExtParam->appId) > 0
          && strlen(pExtParam->deviceSpecial) > 0)
        && !(strlen(pWeilaCtx->appKey) > 0 && strlen(pWeilaCtx->appId) > 0 && strlen(pWeilaCtx->deviceSpecial) > 0)) {
        pbResult.errCode = ERR_INVALID_PARAMS;
        return pbResult;
    }

    pReqLoginApp                        = &wlLoginLoginMessage.reqLoginApp;
    wlLoginLoginMessage.has_reqLoginApp = true;

    pSignature = PB_NULL;

    memset(&hooks, 0, sizeof(WLcJSON_Hooks));
    hooks.malloc_fn = pWeilaCtx->pbFunHook.allocMem;
    hooks.free_fn   = pWeilaCtx->pbFunHook.freeMem;
    WLcJSON_InitHooks(&hooks);
    signatureJson = WLcJSON_CreateObject();
    if (signatureJson) {
        PB_CHAR   timeStamp[MAX_URL_STRING_LEN]    = {0};
        PB_CHAR   signatureMd5[MAX_URL_STRING_LEN] = {0};
        PB_UINT32 timeStampValue                   = pWeilaCtx->pbFunHook.getTimeStamp();
        sprintf(timeStamp, "%u", timeStampValue);

        WLcJSON_AddStringToObject(signatureJson, "et", timeStamp);

        if (pExtParam && strlen(pExtParam->appKey) > 0 && strlen(pExtParam->appId) > 0) {
            WLcJSON_AddStringToObject(signatureJson, "app_id", pExtParam->appId);
            strcpy(&timeStamp[strlen(timeStamp)], pExtParam->appKey);
        } else if (strlen(pWeilaCtx->appKey) > 0 && strlen(pWeilaCtx->appId) > 0) {
            WLcJSON_AddStringToObject(signatureJson, "app_id", pWeilaCtx->appId);
            strcpy(&timeStamp[strlen(timeStamp)], pWeilaCtx->appKey);
        }

        MD5_Calculate(timeStamp, strlen(timeStamp), signatureMd5);
        WLcJSON_AddStringToObject(signatureJson, "sign", signatureMd5);

        pSignature = WLcJSON_PrintUnformatted(signatureJson);
        WLcJSON_Delete(signatureJson);
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
        pbResult.pErrMsg = "create json object fail";
        return pbResult;
    }

    pReqLoginApp->has_pduVersion = true;
    pReqLoginApp->pduVersion     = 4;

    pReqLoginApp->has_clientType = true;
    pReqLoginApp->clientType     = type;

    pReqLoginApp->has_configVersion = true;
    pReqLoginApp->configVersion     = pParam->userConfigVersion;

    weilapb_initEncDataItem(&signatureField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(pSignature), (PB_UINT8*)pSignature);
    weilapb_setEncCallback(&signatureField, &pReqLoginApp->signature, PB_NULL);

    if (strlen(pParam->account) > 0) {
        weilapb_initEncDataItem(
            &accountField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(pParam->account), (PB_UINT8*)pParam->account);
        weilapb_setEncCallback(&accountField, &pReqLoginApp->account, PB_NULL);
    }

    if (strlen(pParam->password) > 0) {
        if (pParam->passwordWithMD5) {
            strcpy(passwdMd5, pParam->password);
        } else {
            MD5_Calculate(pParam->password, strlen(pParam->password), passwdMd5);
        }

        weilapb_initEncDataItem(&passwdField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(passwdMd5), (PB_UINT8*)passwdMd5);
        weilapb_setEncCallback(&passwdField, &pReqLoginApp->password, PB_NULL);
    }

    if (strlen(pParam->countryCode) > 0) {
        weilapb_initEncDataItem(&countryCodeField,
                                PB_DATA_TYPE_FIXED_SIZE_STRING,
                                strlen(pParam->countryCode),
                                (PB_UINT8*)pParam->countryCode);
        weilapb_setEncCallback(&countryCodeField, &pReqLoginApp->countryCode, PB_NULL);
    }

    if (pExtParam || strlen(pWeilaCtx->deviceSpecial)) {
        if (pExtParam) {
            weilapb_initEncDataItem(&specialField,
                                    PB_DATA_TYPE_FIXED_SIZE_STRING,
                                    strlen(pExtParam->deviceSpecial),
                                    (PB_UINT8*)pExtParam->deviceSpecial);
        } else {
            weilapb_initEncDataItem(&specialField,
                                    PB_DATA_TYPE_FIXED_SIZE_STRING,
                                    strlen(pWeilaCtx->deviceSpecial),
                                    (PB_UINT8*)pWeilaCtx->deviceSpecial);
        }
        weilapb_setEncCallback(&specialField, &pReqLoginApp->special, PB_NULL);
    }

    if ((pExtParam && strlen(pExtParam->deviceInfo) > 0) || strlen(pWeilaCtx->deviceInfo) > 0) {
        if (pExtParam) {
            weilapb_initEncDataItem(&devVersionField,
                                    PB_DATA_TYPE_FIXED_SIZE_STRING,
                                    strlen(pExtParam->deviceInfo),
                                    (PB_UINT8*)pExtParam->deviceInfo);
        } else {
            weilapb_initEncDataItem(&devVersionField,
                                    PB_DATA_TYPE_FIXED_SIZE_STRING,
                                    strlen(pWeilaCtx->deviceInfo),
                                    (PB_UINT8*)pWeilaCtx->deviceInfo);
        }
        weilapb_setEncCallback(&devVersionField, &pReqLoginApp->deviceInfo, PB_NULL);
    }

    pbResult = weilapb_buildLogin(WL_Login_LoginCommandId_LOGIN_COMMAND_LOGIN_APP, &wlLoginLoginMessage);

    WLcJSON_free(pSignature);

    return pbResult;
}

PBEncResult weilapb_buildLogoutReq() {
    WL_Login_LoginMessage wlLoginLoginMessage = WL_Login_LoginMessage_init_zero;
    wlLoginLoginMessage.has_reqLogout         = true;
    return weilapb_buildLogin(WL_Login_LoginCommandId_LOGIN_COMMAND_LOGOUT, &wlLoginLoginMessage);
}

PBEncResult weilapb_buildHeartbeatReq() {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_LOGIN,
                                WL_Login_LoginCommandId_LOGIN_COMMAND_HEARTBEAT,
                                WL_Service_CommandType_COMMAND_REQUEST);

    if (pServiceMessage) {
        pServiceMessage->has_loginMessage              = true;
        pServiceMessage->loginMessage.has_reqHeartbeat = true;
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        }
        PB_FREE(pServiceMessage);
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildHeartbeatWithIntervalReq(PB_UINT32 interval) {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_LOGIN,
                                WL_Login_LoginCommandId_LOGIN_COMMAND_HEARTBEAT,
                                WL_Service_CommandType_COMMAND_REQUEST);

    if (pServiceMessage) {
        pServiceMessage->has_loginMessage              = true;
        pServiceMessage->loginMessage.has_reqHeartbeat = true;
        if (interval > 0) {
            pServiceMessage->loginMessage.reqHeartbeat.has_interval = true;
            pServiceMessage->loginMessage.reqHeartbeat.interval     = interval;
        }
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        }
        PB_FREE(pServiceMessage);
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildHeartbeatRsp() {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_LOGIN,
                                WL_Login_LoginCommandId_LOGIN_COMMAND_HEARTBEAT,
                                WL_Service_CommandType_COMMAND_RESPONSE);

    if (pServiceMessage) {
        pServiceMessage->has_loginMessage              = true;
        pServiceMessage->loginMessage.has_reqHeartbeat = true;
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        }
        PB_FREE(pServiceMessage);
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildRefreshAccessTokenReq(void) {
    PBEncResult                pbResult;
    WL_Service_ServiceMessage* pServiceMessage =
        weilapb_buildServiceMsg(WL_Service_ServiceID_SERVICE_LOGIN,
                                WL_Login_LoginCommandId_LOGIN_COMMAND_REFRESH_ACCESS_TOKEN,
                                WL_Service_CommandType_COMMAND_REQUEST);

    if (pServiceMessage) {
        pServiceMessage->has_loginMessage                       = true;
        pServiceMessage->loginMessage.has_reqRefreshAccessToken = true;
        if (weilapb_encodeWeilaMsg(pServiceMessage, &pbResult)) {
            PB_FREE(pServiceMessage);
            return pbResult;
        }
        PB_FREE(pServiceMessage);
    } else {
        pbResult.errCode = ERR_OUT_OF_MEMORY;
    }

    return pbResult;
}

PBEncResult weilapb_buildAnswerShutdownExtensionReq(PB_UINT32 guardianId) {
    WL_Login_LoginMessage wlLoginLoginMessage          = WL_Login_LoginMessage_init_zero;
    wlLoginLoginMessage.has_reqAnswerShutdownExtension = true;

    wlLoginLoginMessage.reqAnswerShutdownExtension.has_guardianId = true;
    wlLoginLoginMessage.reqAnswerShutdownExtension.guardianId     = guardianId;

    return weilapb_buildLogin(WL_Login_LoginCommandId_LOGIN_COMMAND_ANSWER_SHUTDOWN_EXTENSION, &wlLoginLoginMessage);
}

PBEncResult weilapb_buildAnswerBindExtensionReq(PB_UINT32 binderId, PB_CHAR* pVerificationCode, PB_UINT32 status) {
    PBFieldData           verification;
    WL_Login_LoginMessage wlLoginLoginMessage      = WL_Login_LoginMessage_init_zero;
    wlLoginLoginMessage.has_reqAnswerBindExtension = true;

    wlLoginLoginMessage.reqAnswerBindExtension.has_binderId = true;
    wlLoginLoginMessage.reqAnswerBindExtension.binderId     = binderId;

    wlLoginLoginMessage.reqAnswerBindExtension.has_status = true;
    wlLoginLoginMessage.reqAnswerBindExtension.status     = status;

    weilapb_initEncDataItem(
        &verification, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(pVerificationCode), (PB_UINT8*)pVerificationCode);
    weilapb_setEncCallback(&verification, &wlLoginLoginMessage.reqAnswerBindExtension.verificationCode, PB_NULL);

    return weilapb_buildLogin(WL_Login_LoginCommandId_LOGIN_COMMAND_ANSWER_BIND_EXTENSION, &wlLoginLoginMessage);
}

PBEncResult weilapb_buildSetExtensionConfigReq(PB_CHAR* name, PB_CHAR* content) {
    PBFieldData           nameField;
    PBFieldData           contentField;
    WL_Login_LoginMessage wlLoginLoginMessage     = WL_Login_LoginMessage_init_zero;
    wlLoginLoginMessage.has_reqSetExtensionConfig = true;

    weilapb_initEncDataItem(&nameField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(name), (PB_UINT8*)name);
    weilapb_setEncCallback(&nameField, &wlLoginLoginMessage.reqSetExtensionConfig.name, PB_NULL);
    weilapb_initEncDataItem(&contentField, PB_DATA_TYPE_FIXED_SIZE_STRING, strlen(content), (PB_UINT8*)content);
    weilapb_setEncCallback(&contentField, &wlLoginLoginMessage.reqSetExtensionConfig.content, PB_NULL);

    return weilapb_buildLogin(WL_Login_LoginCommandId_LOGIN_COMMAND_SET_EXTENSION_CONFIG, &wlLoginLoginMessage);
}